package com.Pure;

import java.util.Stack;

import javax.microedition.khronos.opengles.GL10;

import com.e3roid.E3Scene;
import com.e3roid.drawable.*;

import android.graphics.*;
import android.view.MotionEvent;
public class PureGrid extends Layer{

	private static int gridSize;
	private Sprite gridSprite;
  
	//a virtual container for storing Game Object ,
	//remove elements of container will not make a graphic change
	private PureGameObject[][] gridContainer;    
 
  	//another container to store lightRay
  	//it is because it has overlapping with some game object in gridContainer and 
  	//lightRayContainer need to be removed each update
	private PureLightRay[][] lightRayContainer;
   
	//for synchronize remove and prevent removing drawing object.
	private Stack<PureGameObject> cleanUpStack;
	
	private Layer lightRayLayer;
	
	//update lightway when finished a draw call
	private boolean forceLightUpdate;
	public static int getGridSize(){return gridSize;}
	
	public PureGrid(Sprite gridSprite,int gridSize)
	{
		super();
		
		cleanUpStack = new Stack<PureGameObject>();
		forceLightUpdate = false;
		this.gridSprite = gridSprite;
		this.gridSize = gridSize;
		
		//create 10x10 array;
		gridContainer = new PureGameObject[10][13];
		lightRayContainer = new PureLightRay[10][13];
		for(int i = 0;i<10;i++){
			for(int j = 0;j <13;j++)
			{
				gridContainer[i][j] = null;
				lightRayContainer[i][j] = null;
			}
		}
		lightRayLayer = new Layer();
		this.add(lightRayLayer);
		
		add(gridSprite);
		//this.setAlpha(0);
	}
	//set update light way force regeneration of lightway when finished a draw call
	public void setUpdateLightRay(boolean b)
	{
		forceLightUpdate = b;
	}
	protected void removeSprite(PureGameObject gameobj)
	{
		
		cleanUpStack.push(gameobj);
		
	}
	public void onDraw(GL10 gl)
	{
	
		synchronized(this)
		{
			super.onDraw(gl);
		}
		
		while(!cleanUpStack.empty())
		{
			PureGameObject gameObj = cleanUpStack.pop();
			remove(gameObj.getSprite());
		}
		
		if(forceLightUpdate){
			this.updateLightRay();
			forceLightUpdate = false;
		}
	
		
	}

  //set a specified gameObject with a grid index
	public void setGridIndexPosition(Point index,PureGameObject gameObject)
	{
		Sprite s = gameObject.getSprite();
		s.move(gridSprite.getRealX() + index.x * gridSize, gridSprite.getRealY() + index.y * gridSize);
		

		if(gameObject instanceof PureLightRay)
		{
			s.moveRelativeX(gridSize / 2);
		}
		
	}
	public void addItem(PureGameObject gameObject)
	{
		Sprite s = gameObject.getSprite();
		//s.move(s.getRealX() - s.getRealX() % gridSize , s.getRealY() - s.getRealY() % gridSize);
		Point index = PureMath.calSnaping(gameObject.getPosition(), this.getPosition(), gridSize);
		setGridIndexPosition(index,gameObject);
		
		if(!hasObject(index)){
			gridContainer[index.x][index.y] = gameObject;
		}
		//add to level's layer;
		add(s);
	}
	public void addItemByGridIndex(PureLightSource source)
	{
		addItemByGridIndex(source,new Point(source.x,source.y));
	}
	public void addItemByGridIndex(PureGameObject gameObject,Point index)
	{
		Sprite s = gameObject.getSprite();
		
		setGridIndexPosition(index,gameObject);
	
		if(gridContainer[index.x][index.y] == null)
		{
			gridContainer[index.x][index.y] = gameObject;
		}
		
		add(s);
		
	}
	//return Point(-1,-1) it has not found 
	public Point indexOf(PureGameObject gameObject)
	{
		for(int i = 0;i<10;i++){
			for(int j = 0;j <10;j++)
			{
				if(gridContainer[i][j] == gameObject)
					return new Point(i,j);
			}
		}
		return new Point(-1,-1);
	}
	public void removeObject(PureGameObject gameObject)
	{
		Point index = indexOf(gameObject);
		if(index.x == -1) return;
		
		removeItemByGridIndex(index.x,index.y);
		
	}
	public boolean hasObject(Point indexGrid)
	{
		return gridContainer[indexGrid.x][indexGrid.y] != null;
	}
	public boolean hasObject(PureGameObject obj)
	{
		
		return indexOf(obj).x != -1;
	}
	public PureGameObject getItemByGridIndex(int x,int y)
	{
		return gridContainer[x][y];
	}
	//return false if no object in here.
	public boolean removeItemByGridIndex(int x,int y)
	{
		if(gridContainer[x][y] != null){
			
			removeSprite(gridContainer[x][y]);
			
			gridContainer[x][y] = null;
			return true;
		}
		return false;
	}
	public synchronized void updateIndex(PureGameObject obj)
	{
		Point originalIndex = indexOf(obj);
		Point updatedIndex = PureMath.calSnaping(obj.getPosition(), this.getPosition(), gridSize);
		
		if (originalIndex.x == -1) return;
			
			
		gridContainer[originalIndex.x][originalIndex.y] = null;
		gridContainer[updatedIndex.x][updatedIndex.y] = obj;

	}
	
	public synchronized void removeObjectToItemList(PureGameObject obj)
	{
		Point originalIndex = indexOf(obj);
		if (originalIndex.x == -1) return;
		gridContainer[originalIndex.x][originalIndex.y] = null;
	}
	
	public synchronized void addObjectFromItemList(PureGameObject obj)
	{
		Point originalIndex = indexOf(obj);
		Point updatedIndex = PureMath.calSnaping(obj.getPosition(), this.getPosition(), gridSize);
		//setGridIndexPosition(updatedIndex,obj);
		gridContainer[updatedIndex.x][updatedIndex.y] = obj;
		//System.out.println(updatedIndex.x + " " + updatedIndex.y);
	}
	
	public PureGameObject[][] getGameObjectList()
	{
		return gridContainer;
	}
	
	public boolean isNullIndex(PureGameObject obj)
	{
		Point updatedIndex = PureMath.calSnaping(obj.getPosition(), this.getPosition(), gridSize);
		if(gridContainer[updatedIndex.x][updatedIndex.y] == null)
			return true;
		return false;
	}
	
	//call when somthing block the light Ray or changing positions of mirror.
	// this method will check all lightrays that is reasonable in a grid
	public synchronized void updateLightRay()
	{
		//cause the thread syn problem, all light will be add and remove in a layer 
		this.remove(lightRayLayer);
		lightRayLayer = new Layer();
		this.add(lightRayLayer);
		
		for(int i = 0;i<10;i++)
			for(int j = 0;j <10;j++)
					lightRayContainer[i][j] = null;
				
		for(int i = 0;i<10;i++){
			for(int j = 0;j <10;j++)
			{
				//generate lightray for each lightsource
				if(gridContainer[i][j] != null && (gridContainer[i][j] instanceof PureLightSource))
				{
					PureLightSource source = (PureLightSource)gridContainer[i][j];
					generateLightRay(source);
				}
			}
		}
		
		
	}
	
	
	public void generateLightRay(PureLightSource source)
	{
		generateLightRay(source.x,source.y,source.dir);
	}
	//next checkpoint of lightray ,use to check collision between lightray and other game object
	
	protected void generateLightRay(int x,int y,PureLightDirection dir)
	{

		Point start = new Point(x,y);
		Point end = new Point(x,y);
		PureGameObject collideObject = null;
		
		//store the length of light passed.
		int count = 0;
		for(int i = 0;isObjectInBound(end);i++)
		{
			if(dir == PureLightDirection.LD_NONE) break;
			end = PureLightRay.nextStep(end,dir);
			count++;
			
			collideObject = gridContainer[end.x][end.y];
			
			
			if(collideObject != null && collideObject.isBlockLight)
			{
					//handle light collide of each game object
					dir = collideObject.onLightCollide(this, start, end,count, dir);
				
					count = 0;
			}else if(!isObjectInBound(end))
			{
				
				generateLightWay(start,count,dir);
			}
		}
	
		
	}
	public boolean isObjectInBound(Point x)
	{
		return x.x > 0 && x.x < 9 && x.y > 0 && x.y < 9;
	}
	public Point getPosition()
	{
		return new Point(gridSprite.getRealX(),gridSprite.getRealY());
	}
	public void generateLightWay(Point start,int length,PureLightDirection dir)
	{
		PureGameFactory factory = PureMain.getInstance().factory;
		//create a light ray from start to specified length
		PureLightRay ray = factory.createLightRay(start, length,dir, 2);
		
		setGridIndexPosition(start, ray);
		lightRayContainer[start.x][start.y] = ray;
		lightRayLayer.add(ray.getSprite());
	}
	
	
	
}
